using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Microsoft.Practices.RepositoryFactory.CodeGeneration.ObjectModel.Mappings.Helper;
using Microsoft.Practices.RepositoryFactory.SchemaDiscovery.ObjectModel.Base;
using Microsoft.Practices.RepositoryFactory.CodeGeneration.ObjectModel;
using Microsoft.Practices.RepositoryFactory.CodeGeneration.ObjectModel.Mappings.Enums;
using Microsoft.Practices.RepositoryFactory.CodeGeneration.ObjectModel.Mappings;
using System.Windows.Forms.Design;
using System.Configuration;
using Microsoft.Practices.RecipeFramework.Extensions.CommonHelpers;
using Microsoft.Practices.RepositoryFactory.CustomPages.Helpers;
using Microsoft.Practices.RepositoryFactory.CodeGeneration.ObjectModel.NamingConventions;
using System.Collections;
using Resources=Microsoft.Practices.RepositoryFactory.Properties.Resources;

namespace Microsoft.Practices.RepositoryFactory.CustomPages
{
    /// <summary/>
    public partial class InputMappingFormCP : Form
    {
        private ConnectionStringSettings connectionSettings;
        private OperationType opType = OperationType.InsertOne;
        private Operation operation = Operation.Add;
        private string operationName;
        private Entity entity;

        /// <summary>
        /// Gets or sets the entity.
        /// </summary>
        /// <value>The entity.</value>
        public Entity Entity
        {
            get { return entity; }
            set { entity = value; }
        }

        /// <summary>
        /// Gets or sets the connection settings.
        /// </summary>
        /// <value>The connection settings.</value>
        public ConnectionStringSettings ConnectionSettings
        {
            get { return connectionSettings; }
            set { connectionSettings = value; }
        }

        /// <summary>
        /// Gets or sets the name of the operation.
        /// </summary>
        /// <value>The name of the operation.</value>
        public string OperationName
        {
            get { return operationName; }
            set { operationName = value; }
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="T:InputMappingFormCP"/> class.
        /// </summary>
        /// <param name="connectionSettings">The connection settings.</param>
        /// <param name="entity">The entity.</param>
        /// <param name="opType">Type of the op.</param>
        public InputMappingFormCP(ConnectionStringSettings connectionSettings, Entity entity, OperationType opType)
            :
            this(connectionSettings, entity, opType, Operation.Add, "")
        { }

        /// <summary>
        /// Initializes a new instance of the <see cref="T:InputMappingFormCP"/> class.
        /// </summary>
        /// <param name="connectionSettings">The connection settings.</param>
        /// <param name="entity">The entity.</param>
        /// <param name="opType">Type of the op.</param>
        /// <param name="operation">The operation.</param>
        /// <param name="operationName">Name of the operation.</param>
        public InputMappingFormCP(ConnectionStringSettings connectionSettings, Entity entity, OperationType opType, Operation operation, string operationName)
        {
            InitializeComponent();

            switch(opType)
            {
                case OperationType.UpdateOne:
                    txtOperationType.Text = "Update One";
                    lstOutputParameters.Enabled = false;
                    lstOutputParameters.BackColor = SystemColors.Control;
                    insertControlsPanel.Visible = false;
                    break;
                case OperationType.InsertOne:
                    txtOperationType.Text = "Insert One";
                    lstOutputParameters.Enabled = true;
                    lstOutputParameters.BackColor = SystemColors.Window;
                    break;
                default:
                    break;
            }

            this.entity = entity;
            this.opType = opType;
            this.operation = operation;
            this.operationName = operationName;
            this.connectionSettings = connectionSettings;

            SetControls();
            cmbStoredProcedures.Items.Clear();

            List<StoredProcedure> storedProcedures =
                UIHelper.GetStoredProcedures(connectionSettings);

            Cursor.Current = Cursors.WaitCursor;

            try
            {
                UIHelper.FillStoredProceduresCombo(cmbStoredProcedures, storedProcedures);
            }
            catch
            {
                //We cannot use IUIService on an external winform page
                MessageBox.Show(
                    Microsoft.Practices.RepositoryFactory.Properties.Resources.ErrorObtainingSPList,
                    Microsoft.Practices.RepositoryFactory.Properties.Resources.Error);
            }

            Cursor.Current = Cursors.Default;

            if(this.operation == Operation.Edit)
            {
                ProvideValues();
            }
        }

        private void cmbStoredProcedures_SelectedIndexChanged(object sender, EventArgs e)
        {
            StoredProcedure sp = (StoredProcedure)cmbStoredProcedures.SelectedItem;

            if(this.operation == Operation.Edit)
            {
                foreach(EntityStoredProcedureMapping mapping in this.entity.StoredProcedureMappings)
                {
                    if(mapping.Procedure == sp.Name &&
                       mapping.OperationName == this.operationName)
                    {
                        RefreshMappings(mapping);
                        break;
                    }
                }
            }
            else
            {
                txtOperationName.Text = sp.Name;
                CleanCombos();
                AutoMapProperties(sp);
                FillCombos();
            }
        }

        private void FillCombos()
        {
            FillProperties(this.entity.Properties);

            List<Parameter> parameters = UIHelper.GetParameters(connectionSettings, (StoredProcedure)cmbStoredProcedures.SelectedItem, ParameterDirection.Input);
            FillParameters(lstInputParameters, parameters);

            List<Parameter> parameters1 = UIHelper.GetParameters(connectionSettings, (StoredProcedure)cmbStoredProcedures.SelectedItem, ParameterDirection.Output);
            FillParameters(lstOutputParameters, parameters1);

            List<Parameter> parameters2 = UIHelper.GetParameters(connectionSettings, (StoredProcedure)cmbStoredProcedures.SelectedItem, ParameterDirection.InputOutput);
            FillParameters(lstOutputParameters, parameters2);
        }

        private void FillProperties(List<Property> properties)
        {
            foreach (Property prop in properties)
            {
                if (UIHelper.FindProperty(lstMappings, prop.Name) == null)
                {
                    lstEntityProperties.Items.Add(prop.Name);
                }
            }
        }

        private void FillParameters(ListBox listBox, List<Parameter> parameters)
        {
            List<Parameter> parametersToAdd = new List<Parameter>();
            foreach(Parameter parameter in parameters)
            {
                if(UIHelper.FindParameter(lstMappings, parameter.Name) == null)
                {
                    parametersToAdd.Add(parameter);
                }
            }
            UIHelper.FillParametersListBox(listBox, parametersToAdd);
        }

        private void AutoMapProperties(StoredProcedure sp)
        {
            foreach (Property property in this.entity.Properties)
            {
                foreach (Parameter parameter in sp.Parameters)
                {
                    string paramName = ParseParamName(parameter.Name);
                    if (string.Compare(paramName, ParseParamName(property.Name), true) == 0)
                    {
                        PropertyParameterMapping mapping = new PropertyParameterMapping(property.Name,
                            ClonerHelper.CloneParameter(parameter));
                        
                        lstMappings.Items.Add(mapping);
                    } 
                }
            }
        }
        private string ParseParamName(string originalParamName)
        {
            return originalParamName.Replace("_", string.Empty);
        }

        private void okButton_Click(object sender, EventArgs e)
        {
            if(IsInputValid())
            {
                SaveMapping();
                this.DialogResult = DialogResult.OK;
            }
        }

        private void btnAddMapping_Click(object sender, EventArgs e)
        {
            if(!(UIGuard.IsListBoxSelected(lstEntityProperties) &&
                (UIGuard.IsListBoxSelected(lstInputParameters) ||
                    UIGuard.IsListBoxSelected(lstOutputParameters))))
            {
                return;
            }

            if(UIGuard.IsListBoxSelected(lstEntityProperties) &&
                UIGuard.IsListBoxSelected(lstInputParameters) &&
                UIGuard.IsListBoxSelected(lstOutputParameters))
            {
                return;
            }

            if(UIGuard.IsListBoxSelected(lstEntityProperties) &&
               UIGuard.IsListBoxSelected(lstInputParameters))
            {
                PropertyParameterMapping mapping =
                new PropertyParameterMapping(
                    lstEntityProperties.SelectedItem.ToString(),
                    ClonerHelper.CloneParameter((Parameter)lstInputParameters.SelectedItem));

                lstEntityProperties.Items.Remove(lstEntityProperties.SelectedItem);
                lstInputParameters.Items.Remove(lstInputParameters.SelectedItem);

                lstMappings.Items.Add(mapping);

                lstMappings.SelectedIndex = 0;
            }
            else if(UIGuard.IsListBoxSelected(lstEntityProperties) &&
                    UIGuard.IsListBoxSelected(lstOutputParameters))
            {
                PropertyParameterMapping mapping =
                new PropertyParameterMapping(
                    lstEntityProperties.SelectedItem.ToString(),
                    ClonerHelper.CloneParameter((Parameter)lstOutputParameters.SelectedItem));

                lstEntityProperties.Items.Remove(lstEntityProperties.SelectedItem);
                lstOutputParameters.Items.Remove(lstOutputParameters.SelectedItem);

                lstPrimaryKeyMapping.Items.Add(mapping);

                lstPrimaryKeyMapping.SelectedIndex = 0;
            }
        }

        private void btnDeleteMapping_Click(object sender, EventArgs e)
        {
            if(!UIGuard.IsListBoxChecked(lstMappings) &&
               !UIGuard.IsListBoxChecked(lstPrimaryKeyMapping))
            {
                return;
            }

            UIHelper.RemoveMappingItems(lstEntityProperties, lstInputParameters, lstMappings);
            UIHelper.RemoveMappingItems(lstEntityProperties, lstOutputParameters, lstPrimaryKeyMapping);
        }

        private void txtOperationName_Leave(object sender, EventArgs e)
        {
            ValidateOperationName();
        }

        private void SetControls()
        {
            cmbStoredProcedures.DisplayMember = Resources.StoredProceduresDisplayMember;
            lstInputParameters.DisplayMember = Resources.ParameterNameDisplayMember;
            lstOutputParameters.DisplayMember = Resources.ParameterNameDisplayMember;
            txtEntity.Text = this.entity.Name;
            txtOperationName.Text = string.Empty;
            txtOperationName.Enabled = (this.operation == Operation.Add);
            cmbStoredProcedures.Enabled = (this.operation == Operation.Add);
            txtOperationName.Focus();
        }

        private void ProvideValues()
        {
            UIHelper.Finder = NamingHelper.GetOperationName(this.operationName);
            EntityStoredProcedureMapping mapping = this.entity.StoredProcedureMappings.Find(UIHelper.FindByOperationName);

            if(mapping != null)
            {
                txtOperationName.Text = mapping.OperationName;
                cmbStoredProcedures.SelectedItem = UIHelper.FindStoredProcedure(cmbStoredProcedures, mapping.Procedure);
            }
        }

        private void SaveMapping()
        {
            StoredProcedure sp = (StoredProcedure)cmbStoredProcedures.SelectedItem;
            UIHelper.Finder = NamingHelper.GetOperationName(txtOperationName.Text);
            EntityStoredProcedureMapping spMapping = this.entity.StoredProcedureMappings.Find(UIHelper.FindByOperationName);

            // Check for both IN and IN/OUT params.  If either is true, then 
            // the stored proc has input params.
            bool hasInOrInOutParams =
                (UIHelper.GetParameters(this.connectionSettings, sp, ParameterDirection.Input).Count > 0) ||
                (UIHelper.GetParameters(this.connectionSettings, sp, ParameterDirection.InputOutput).Count > 0);

            if(spMapping == null)
            {
                spMapping =
                    new EntityStoredProcedureMapping(
                    sp.Name,
                    hasInOrInOutParams,
                    NamingHelper.GetOperationName(txtOperationName.Text),
                    this.opType);
            }
            else
            {
                this.entity.StoredProcedureMappings.Remove(spMapping);
                spMapping.Procedure = sp.Name;
                spMapping.ProcedureHasInputParameters = hasInOrInOutParams;
                spMapping.OperationName = NamingHelper.GetOperationName(txtOperationName.Text);
                spMapping.ParameterMappings.Clear();
            }

            foreach(object item in lstMappings.Items)
            {
                PropertyParameterMapping mapping = (PropertyParameterMapping)item;

                spMapping.ParameterMappings.Add(mapping);
            }

            foreach(object item in lstPrimaryKeyMapping.Items)
            {
                PropertyParameterMapping mapping = (PropertyParameterMapping)item;

                spMapping.ParameterMappings.Add(mapping);
            }

            this.entity.StoredProcedureMappings.Add(spMapping);
        }

        private void RefreshMappings(EntityStoredProcedureMapping mapping)
        {
            CleanCombos();

            foreach(PropertyParameterMapping parameterMapping in mapping.ParameterMappings)
            {
                if(parameterMapping.ParameterInstance.Direction == ParameterDirection.Input)
                {
                    lstMappings.Items.Add(parameterMapping);
                }
                else if(parameterMapping.ParameterInstance.Direction == ParameterDirection.Output ||
                    parameterMapping.ParameterInstance.Direction == ParameterDirection.InputOutput)
                {
                    lstPrimaryKeyMapping.Items.Add(parameterMapping);
                }
            }

            foreach(Property prop in this.entity.Properties)
            {
                if(UIHelper.FindProperty(lstMappings, prop.Name) == null &&
                   UIHelper.FindProperty(lstPrimaryKeyMapping, prop.Name) == null)
                {
                    lstEntityProperties.Items.Add(prop.Name);
                }
            }

            List<Parameter> parameters =
                UIHelper.GetParameters(
                    connectionSettings,
                    (StoredProcedure)cmbStoredProcedures.SelectedItem,
                    ParameterDirection.Input);

            UIHelper.FillParametersListBox(lstInputParameters, lstMappings, parameters);

            List<Parameter> parameters1 =
                UIHelper.GetParameters(
                    connectionSettings,
                    (StoredProcedure)cmbStoredProcedures.SelectedItem,
                    ParameterDirection.Output);

            UIHelper.FillParametersListBox(lstOutputParameters, lstPrimaryKeyMapping, parameters1);

            List<Parameter> parameters2 =
                UIHelper.GetParameters(
                    connectionSettings,
                    (StoredProcedure)cmbStoredProcedures.SelectedItem,
                    ParameterDirection.InputOutput);

            UIHelper.FillParametersListBox(lstOutputParameters, lstPrimaryKeyMapping, parameters2);
        }

        private void LoadMappings()
        {
            //TODO: Not empty in my older copy of DAAG/Oracle.  Action TBD.

           
        }

        private void CleanCombos()
        {
            lstEntityProperties.Items.Clear();
            lstInputParameters.Items.Clear();
            lstOutputParameters.Items.Clear();
            lstMappings.Items.Clear();
            lstPrimaryKeyMapping.Items.Clear();
        }

        private bool ValidateOperationName()
        {
            frmErrorProvider.SetError(txtOperationName, null);
            try
            {
                txtOperationName.Text = CodeDomHelper.ConvertToValidIdentifier(txtOperationName.Text);
                return true;
            }
            catch(Exception ex)
            {
                frmErrorProvider.SetError(txtOperationName, ex.Message);
                return false;
            }
        }

        private bool IsInputValid()
        {
            bool response = true;
            frmErrorProvider.SetError(txtOperationName, "");
            frmErrorProvider.SetError(cmbStoredProcedures, "");
            frmErrorProvider.SetError(lstMappings, "");
            frmErrorProvider.SetError(lstPrimaryKeyMapping, "");

            if(this.operation == Operation.Add)
            {
                if (string.IsNullOrEmpty(NamingHelper.GetOperationName(txtOperationName.Text)))
                {
                    frmErrorProvider.SetError(txtOperationName, Resources.ProvideOpName);
                    response = false;
                }
                else if (!ValidateOperationName())
                {
                    response = false;
                }

                UIHelper.Finder = NamingHelper.GetOperationName(txtOperationName.Text);
                EntityStoredProcedureMapping mapping = this.entity.StoredProcedureMappings.Find(UIHelper.FindByOperationName);

                if(mapping != null)
                {
                    frmErrorProvider.SetError(txtOperationName, Resources.MappingWithSameOperationName);
                    response = false;
                }
                else
                {
                    UIHelper.Finder = ((StoredProcedure)cmbStoredProcedures.SelectedItem).Name;
                    mapping = this.entity.StoredProcedureMappings.Find(UIHelper.FindByStoredProcedureName);

                    if(mapping != null)
                    {
                        frmErrorProvider.SetError(cmbStoredProcedures, Resources.MappingWithSameSPName);
                        response = false;
                    }
                }
            }

            if(cmbStoredProcedures.SelectedItem == null)
            {
                frmErrorProvider.SetError(cmbStoredProcedures, Resources.ProvideSP);
                response = false;
            }

            if(lstMappings.Items.Count == 0 && lstPrimaryKeyMapping.Items.Count == 0)
            {
                frmErrorProvider.SetError(lstMappings, Resources.ProvideMapping);
                frmErrorProvider.SetError(lstPrimaryKeyMapping, Resources.ProvideMapping);
                response = false;
            }

            return response;
        }

        private void btnCancel_Click(object sender, EventArgs e)
        {
            this.DialogResult = DialogResult.Cancel;
            this.Close();
        }
    }
}
